﻿using Generic.Helper.JsonConverter;
using Itech_Run.Helper.Configure;
using Itech_Run.HttpApi.Host.Filter;
using Itech_Run.HttpApi.Shared.Options;
using Itech_Run.Sys.HttpApi.Middleware;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;
using System.Globalization;
using System.Reflection;
using System.Text.Json;
using Volo.Abp;
using Volo.Abp.AspNetCore.Mvc.ExceptionHandling;
using Volo.Abp.Modularity;

namespace Itech_Run.HttpApi.Host
{
    [DependsOn(typeof(Volo.Abp.Autofac.AbpAutofacModule),
        typeof(Sys.HttpApi.Itech_RunSysHttpApiModule),
        typeof(Sys.Application.Itech_RunSysApplicationModule),
        typeof(Blog.HttpApi.Itech_RunBlogHttpApiModule),
        typeof(Blog.Application.Itech_RunBlogApplicationModule),
        typeof(Itech_Run.Sys.EntityFrameworkCore.Itech_RunSysEntityFrameworkCoreModule),
        typeof(Itech_Run.Blog.EntityFrameworkCore.Itech_RunBlogEntityFrameworkCoreModule))]
    public class Itech_RunHttpApiHostModule : AbpModule
    {
        private readonly string _devAllowSpecificOrigins = "_devAllowSpecificOrigins";
        public override void ConfigureServices(ServiceConfigurationContext context)
        {
            var configuration = context.Services.GetConfiguration();
            var services = context.Services;
            services.Configure<JwtOptions>(configuration.GetSection(JwtOptions.Position));
            services.Configure<AesOptions>(configuration.GetSection(AesOptions.Position));
            services.Configure<OtherOptions>(configuration.GetSection(OtherOptions.Position));
            services.Configure<CacheOptions>(configuration.GetSection(CacheOptions.Position));

            ConfigureSwaggerServices(services);
            ConfigureMvc(services);
            services.JwtConfigure(configuration);

            services.AddCors(options =>
            {
                if (configuration.GetSection("Cors:IsOpen").Value == "1")
                {
                    options.AddPolicy("AllowOrigin", options => options.AllowAnyOrigin());
                }
                else
                {
                    options.AddPolicy(_devAllowSpecificOrigins,
                    builder =>
                    {
                        builder.WithOrigins(getCorsItems(configuration, "Origins"))
                        .WithHeaders(getCorsItems(configuration, "Headers"))
                        .WithMethods(getCorsItems(configuration, "Methods"));
                    });
                }
            });
        }

        private string[] getCorsItems(IConfiguration configuration, string section)
        {
            return configuration.GetSection($"Cors:{section}").Value.Split(",");
        }

        private static void ConfigureSwaggerServices(IServiceCollection services)
        {
            services.AddSwaggerGen(
                options =>
                {
                    options.SwaggerGeneratorOptions.DescribeAllParametersInCamelCase = true;
                    options.SwaggerDoc("v1", new OpenApiInfo { Title = "Itech_Run API", Version = "v1" });
                    options.DocInclusionPredicate((docName, description) => true);
                    options.CustomSchemaIds(type => type.FullName);
                    options.DocumentFilter<SwaggerDocSchemaFilter>();
                    options.OperationFilter<SwaggerOperationFilter>();

                    //添加XML注释文件
                    Directory.GetFiles(AppContext.BaseDirectory, "*.xml").ToList().ForEach(xmlFile =>
                    {
                        options.IncludeXmlComments(xmlFile);
                    });

                    var openApiSecurity = new OpenApiSecurityScheme()
                    {
                        Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
                        Name = "Authorization",
                        In = ParameterLocation.Header,
                        Type = SecuritySchemeType.ApiKey
                    };
                    // Define the BearerAuth scheme that's in use
                    options.AddSecurityDefinition("bearerAuth", openApiSecurity);
                    //Token认证方式，此方式为全局添加
                    options.AddSecurityRequirement(new OpenApiSecurityRequirement
                    {
                        {
                            new OpenApiSecurityScheme
                            {
                                Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "bearerAuth" }
                            },
                            new List<string>()
                        }
                    });
                });
        }

        private void ConfigureMvc(IServiceCollection services)
        {
            var policy = new AuthorizationPolicyBuilder()
            .RequireAuthenticatedUser()
            .Build();

            services.AddMvc(options =>
            {
                options.Filters.Add(new AuthorizeFilter(policy));
                options.Filters.Add(new IgnoreAntiforgeryTokenAttribute());
            }).AddJsonOptions(options =>
            {
                options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;

                var jsonConverters = options.JsonSerializerOptions.Converters;
                for (var i = jsonConverters.Count - 1; i >= 0; i--)
                {
                    if (jsonConverters[i].GetType() == typeof(Volo.Abp.Json.SystemTextJson.JsonConverters.AbpDateTimeConverter))
                    {
                        jsonConverters.RemoveAt(i);
                    }
                    else if (jsonConverters[i].GetType() == typeof(Volo.Abp.Json.SystemTextJson.JsonConverters.AbpNullableDateTimeConverter))
                    {
                        jsonConverters.RemoveAt(i);
                    }
                }
                options.JsonSerializerOptions.Converters.Add(new DateTimeConverter());
                options.JsonSerializerOptions.Converters.Add(new DateTimenNullConverter());
            });
        }

        private void SetStaticFolder(IApplicationBuilder app)
        {
            var provider = new FileExtensionContentTypeProvider();
            //添加.msg格式
            provider.Mappings[".msg"] = "application/octet-stream";

            var directorypath = Directory.GetCurrentDirectory() + "\\Files";
            if (!Directory.Exists(directorypath)) Directory.CreateDirectory(directorypath);
            //提供文件目录静态文件访问
            app.UseStaticFiles(new StaticFileOptions()
            {
                FileProvider = new PhysicalFileProvider(directorypath),
                RequestPath = new PathString("/Files"),
                ContentTypeProvider = provider
            });
        }

        public override void OnApplicationInitialization(ApplicationInitializationContext context)
        {
            var app = context.GetApplicationBuilder();
            var env = context.GetEnvironment();

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            //移除ABP自带的异常拦截器
            var ops = app.ApplicationServices.GetRequiredService<IOptions<MvcOptions>>().Value;
            var abpExceptionFilters = ops.Filters.FirstOrDefault(f => (f as ServiceFilterAttribute)?.ServiceType == (typeof(AbpExceptionFilter)));
            if (abpExceptionFilters != null)
                ops.Filters.Remove(abpExceptionFilters);

            SetStaticFolder(app);
            app.UseStaticFiles();
            app.UseRouting();
            app.UseCors(_devAllowSpecificOrigins);

            app.UseMiddleware<ExceptionMiddleware>(app);
            app.UseMiddleware<TokenGreyMiddleware>(app);
            app.UseMiddleware<AutoAuthMiddleware>();

            app.UseAuthentication();
            app.UseAbpRequestLocalization(options =>
            {
                options.DefaultRequestCulture = new RequestCulture("zh-Hans");
            });

            //多语言
            app.Use((context, next) =>
            {
                var cultureName = context.Request.Headers["Culture"].ToString();
                if (!string.IsNullOrEmpty(cultureName))
                {
                    CultureInfo.CurrentCulture = new CultureInfo(cultureName);
                    CultureInfo.CurrentUICulture = new CultureInfo(cultureName);
                }
                return next.Invoke();
            });

            app.UseAuthorization();

            Func<string, string, string, object[]?> moduleAttributeDelegate = methodAttributeResolve;
            app.UseMiddleware<ModuleAuthMiddleware>(app, moduleAttributeDelegate);
            Func<string, string, string, object[]?> btnAttributeDelegate = methodAttributeResolve;
            app.UseMiddleware<ButtonAuthMiddleware>(app, btnAttributeDelegate);

            app.UseSwagger();
            app.UseSwaggerUI(options =>
            {
                string urlPrefix = context.GetConfiguration().GetSection("Swagger:UrlPrefix").Value;
                options.SwaggerEndpoint($"{urlPrefix}/swagger/v1/swagger.json", "Itech_Run API");
                options.InjectStylesheet("/css/Swagger_Custom.css");
                options.InjectJavascript("/js/Swagger_Custom.js");
            });
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
                endpoints.MapControllerRoute("defaultWithArea", "{area}/{controller=Home}/{action=Index}/{id?}");
            });
        }

        private object[]? methodAttributeResolve(string assemblyName, string controllerName, string actionName)
        {
            var customAttributes = Type.GetType($"{assemblyName}.Controllers.{controllerName},{assemblyName}")?
                .GetMethod(actionName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance)?.GetCustomAttributes(false);
            if (customAttributes is null)
                customAttributes = Type.GetType($"{assemblyName}.Controllers.{controllerName},{assemblyName}")?
                    .GetMethod(actionName + "Async", BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance)?.GetCustomAttributes(false);
            return customAttributes;
        }

    }

}
